/*
 * string manipulation functions for Py_UNICODE strings with set operands 
 * limited to ascii range (0 - 127)
 */

#ifdef HAVE_UINT64_T
#define BITVEC_T PY_UINT64_T
#else
#define BITVEC_T PY_UINT32_T
#endif

#define BITVEC_LEN (sizeof(BITVEC_T)*8)
#define BITVEC_IDX(c) ((unsigned)(c) / BITVEC_LEN)
#define BITVEC_BIT(c) ((BITVEC_T) 1 << ((unsigned)(c) % BITVEC_LEN))

#define ASCII_MAX 127U
#define ASCII_UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define ASCII_LOWER "abcdefghijklmnopqrstuvwxyz"
#define ASCII_DIGITS "0123456789"
#define ASCII_HEXDIGITS "0123456789ABCDEFabcdef"
#define ASCII_ALPHA ASCII_UPPER ASCII_LOWER
#define ASCII_ALNUM ASCII_ALPHA ASCII_DIGITS

Py_LOCAL(Py_ssize_t)
Py_unispn(const Py_UNICODE *uni, Py_ssize_t len, const char *set)
{
	BITVEC_T vec[(ASCII_MAX + 1) / BITVEC_LEN] = {0};
	BITVEC_T bit;
	unsigned idx;
	Py_ssize_t cnt;

	if (len == 0)
		return 0;
	for (; *set != 0; set++)
	{
		assert((unsigned) *set <= ASCII_MAX);
		idx = BITVEC_IDX(*set);
		bit = BITVEC_BIT(*set);
		vec[idx] |= bit;
	}
	for (cnt = 0; cnt < len; cnt++)
	{
		if (uni[cnt] > ASCII_MAX)
			break;
		idx = BITVEC_IDX(uni[cnt]);
		bit = BITVEC_BIT(uni[cnt]);
		if ((vec[idx] & bit) == 0)
			break;
	}
	return cnt;
}

Py_LOCAL(Py_ssize_t)
Py_unicspn(const Py_UNICODE *uni, Py_ssize_t len, const char *set)
{
	BITVEC_T vec[(ASCII_MAX + 1) / BITVEC_LEN] = {0};
	BITVEC_T bit;
	unsigned idx;
	Py_ssize_t cnt;

	if (len == 0)
		return 0;
	for (; *set != 0; set++)
	{
		assert((unsigned) *set <= ASCII_MAX);
		idx = BITVEC_IDX(*set);
		bit = BITVEC_BIT(*set);
		vec[idx] |= bit;
	}
	for (cnt = 0; cnt < len; cnt++)
	{
		if (uni[cnt] > ASCII_MAX)
			continue;
		idx = BITVEC_IDX(uni[cnt]);
		bit = BITVEC_BIT(uni[cnt]);
		if ((vec[idx] & bit) != 0)
			break;
	}
	return cnt;
}

Py_LOCAL_INLINE(Py_UNICODE *)
Py_unipbrk(const Py_UNICODE *uni, Py_ssize_t len, const char *set)
{
	Py_ssize_t cnt = Py_unicspn(uni, len, set);

	if (cnt < len)
		return (Py_UNICODE *) (uni + cnt);
	else
		return NULL;
}

Py_LOCAL_INLINE(Py_UNICODE *)
Py_unichr(const Py_UNICODE *uni, Py_ssize_t len, const char ch)
{
	Py_ssize_t cnt;

	assert((unsigned) ch <= ASCII_MAX);
	for (cnt = 0; cnt < len; cnt++)
		if (uni[cnt] == ch)
			return (Py_UNICODE *) (uni + cnt);
	return NULL;
}
